#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
项目启动脚本
支持后端API服务和前端开发服务器的一键启动
"""

import os
import sys
import subprocess
import signal
import time
import platform

# 添加项目根目录到Python路径
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)

def check_port_availability(port):
    """检查端口是否可用"""
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    result = sock.connect_ex(('127.0.0.1', port))
    sock.close()
    return result != 0

def install_python_dependencies():
    """安装Python依赖"""
    print("正在检查并安装Python依赖...")
    try:
        subprocess.run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"], 
                      check=True, cwd=project_root)
        print("Python依赖安装完成")
        return True
    except subprocess.CalledProcessError as e:
        print(f"Python依赖安装失败: {e}")
        return False
    except Exception as e:
        print(f"安装Python依赖时发生错误: {e}")
        return False

def start_backend():
    """启动后端服务"""
    print("正在启动后端服务...")
    
    # 安装Python依赖
    if not install_python_dependencies():
        print("Python依赖安装失败，无法启动后端服务")
        return None
    
    # 尝试端口
    ports = [11451]
    backend_process = None
    
    for port in ports:
        if check_port_availability(port):
            print(f"正在尝试启动后端服务，端口: {port}")
            try:
                # 更改工作目录到app目录
                app_dir = os.path.join(project_root, "app")
                if not os.path.exists(app_dir):
                    print("错误: 后端app目录不存在")
                    return None
                
                os.chdir(app_dir)
                
                # 启动后端服务
                backend_process = subprocess.Popen([
                    sys.executable, "-m", "uvicorn", 
                    "app:app", 
                    "--host", "127.0.0.1", 
                    "--port", str(port), 
                    "--reload"
                ])
                
                print(f"后端服务已启动，端口: {port}")
                print(f"API文档地址: http://127.0.0.1:{port}/docs")
                break
            except Exception as e:
                print(f"端口 {port} 启动失败: {e}")
                continue
        else:
            print(f"警告: 端口 {port} 已被占用")
            # 检查是否是之前的进程占用
            try:
                if platform.system() == "Windows":
                    result = subprocess.run(["netstat", "-ano"], capture_output=True, text=True)
                    lines = result.stdout.split('\n')
                    for line in lines:
                        if f":{port}" in line and "LISTENING" in line:
                            parts = line.split()
                            pid = parts[-1]
                            print(f"端口 {port} 被PID为 {pid} 的进程占用")
                            # 询问用户是否终止进程
                            choice = input(f"是否终止占用端口 {port} 的进程? (y/N): ")
                            if choice.lower() == 'y':
                                try:
                                    subprocess.run(["taskkill", "/PID", pid, "/F"])
                                    print(f"已终止占用端口 {port} 的进程 (PID: {pid})")
                                    # 等待进程终止
                                    time.sleep(1)
                                    # 重新尝试启动
                                    backend_process = subprocess.Popen([
                                        sys.executable, "-m", "uvicorn", 
                                        "app:app", 
                                        "--host", "127.0.0.1", 
                                        "--port", str(port), 
                                        "--reload"
                                    ])
                                    print(f"后端服务已启动，端口: {port}")
                                    print(f"API文档地址: http://127.0.0.1:{port}/docs")
                                    break
                                except Exception as e:
                                    print(f"终止进程失败: {e}")
                else:
                    print("在非Windows系统上无法自动终止进程，请手动处理端口占用问题")
            except Exception as e:
                print(f"检查端口占用情况时出错: {e}")
                continue
    
    if backend_process is None:
        print("所有端口都无法使用，请检查是否有防火墙或其他程序阻止了端口绑定")
        return None
    
    # 切换回项目根目录
    os.chdir(project_root)
    
    return backend_process

def check_node_environment():
    """检查Node.js环境"""
    npm_executable = None
    try:
        # 首先尝试直接运行npm
        subprocess.run(["npm", "--version"], check=True, capture_output=True)
        npm_executable = "npm"
        print("找到npm命令")
    except (subprocess.CalledProcessError, FileNotFoundError):
        try:
            # 在Windows上尝试使用npm.cmd
            subprocess.run(["npm.cmd", "--version"], check=True, capture_output=True)
            npm_executable = "npm.cmd"
            print("找到npm.cmd命令")
        except (subprocess.CalledProcessError, FileNotFoundError):
            print("警告: 未找到npm命令，请确保已安装Node.js")
            return None
    return npm_executable

def start_frontend():
    """启动前端服务"""
    print("正在启动前端服务...")
    
    # 检查Node.js环境
    npm_executable = check_node_environment()
    if npm_executable is None:
        print("Node.js环境检查失败，无法启动前端服务")
        return None
    
    # 检查前端目录是否存在
    frontend_dir = os.path.join(project_root, "frontend")
    if not os.path.exists(frontend_dir):
        print("警告: 前端目录不存在")
        return None
    
    # 检查package.json是否存在
    package_json = os.path.join(frontend_dir, "package.json")
    if not os.path.exists(package_json):
        print("警告: 前端package.json文件不存在")
        return None
    
    try:
        # 切换到前端目录
        os.chdir(frontend_dir)
        
        # 使用自定义脚本启动前端（会自动检查并安装依赖）
        frontend_process = subprocess.Popen([npm_executable, "start"])
        print("前端服务启动中...")
        print("前端地址: http://localhost:19198")
        return frontend_process
    except Exception as e:
        print(f"启动前端服务时出错: {e}")
        return None

def print_service_status(backend_process, frontend_process):
    """打印服务状态"""
    print("\n" + "=" * 40)
    print("服务状态:")
    if backend_process and backend_process.poll() is None:
        print("✓ 后端API服务运行中")
    else:
        print("✗ 后端API服务未运行")
        
    if frontend_process and frontend_process.poll() is None:
        print("✓ 前端服务运行中")
    else:
        print("✗ 前端服务未运行")
    print("=" * 40)

def main():
    """主函数"""
    print("数据库大作业项目启动脚本")
    print("=" * 40)
    
    # 切换回项目根目录
    os.chdir(project_root)
    
    # 启动后端服务
    backend_process = start_backend()
    
    if backend_process is None:
        print("后端服务启动失败")
        return
    
    # 等待后端服务启动
    print("等待后端服务启动...")
    time.sleep(3)
    
    # 切换回项目根目录再启动前端
    os.chdir(project_root)
    
    # 启动前端服务
    frontend_process = start_frontend()
    
    def signal_handler(sig, frame):
        """信号处理函数，用于优雅关闭服务"""
        print("\n正在关闭服务...")
        if backend_process and backend_process.poll() is None:
            backend_process.terminate()
            try:
                backend_process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                backend_process.kill()
        if frontend_process and frontend_process.poll() is None:
            frontend_process.terminate()
            try:
                frontend_process.wait(timeout=5)
            except subprocess.TimeoutExpired:
                frontend_process.kill()
        print("服务已关闭")
        sys.exit(0)
    
    # 注册信号处理器
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)
    
    print_service_status(backend_process, frontend_process)
    print("使用 Ctrl+C 可以停止所有服务")
    
    try:
        # 等待进程结束
        while True:
            # 检查进程是否仍在运行
            if (backend_process and backend_process.poll() is not None) or \
               (frontend_process and frontend_process.poll() is not None):
                print("\n检测到服务异常退出:")
                print_service_status(backend_process, frontend_process)
                break
            time.sleep(1)
    except KeyboardInterrupt:
        signal_handler(signal.SIGINT, None)

if __name__ == "__main__":
    main()